import java.util.List;
import java.util.LinkedList;

public class TinyGA {
   public TinyGA() {
   } 
    
   public abstract class Sent  {
     protected Sent() {
       err = new Atributo<Error>();
       err.fijaExpresion(
			new ExpSem<Error>() {
					public Error val() {
							return err_exp();
       }});               
       cod = new Atributo<List<Instruccion>>();
       cod.fijaExpresion(new ExpSem<List<Instruccion>>() {public List<Instruccion> val() {
           return cod_exp();
       }});               
     }         
     public Atributo<Error> err() {return err;}
     public Atributo<List<Instruccion>> cod() {return cod;}
     protected abstract Error err_exp();
     protected abstract List<Instruccion> cod_exp();
     private Atributo<Error> err;
     private Atributo<List<Instruccion>> cod;
   }      
      
   public abstract class Decs  {
     protected Decs() {
       err = new Atributo<Error>();
       err.fijaExpresion(new ExpSem<Error>() {public Error val() {
           return err_exp();
       }});               
       ts = new Atributo<TS>();
       ts.fijaExpresion(new ExpSem<TS>() {public TS val() {
           return ts_exp();
       }});               
     }         
     public Atributo<Error> err() {return err;}
     public Atributo<TS> ts() {return ts;}
     protected abstract Error err_exp();
     protected abstract TS ts_exp();
     private Atributo<Error> err;
     private Atributo<TS> ts;
   }      

   public abstract class Dec  {
     protected Dec() {
       cte = new Atributo<String>();
       cte.fijaExpresion(new ExpSem<String>() {public String val() {
           return cte_exp();
       }});               
       val = new Atributo<String>();
       val.fijaExpresion(new ExpSem<String>() {public String val() {
           return val_exp();
       }});               
       fila = new Atributo<Integer>();
       fila.fijaExpresion(new ExpSem<Integer>() {public Integer val() {
           return fila_exp();
       }});               
       col = new Atributo<Integer>();
       col.fijaExpresion(new ExpSem<Integer>() {public Integer val() {
           return col_exp();
       }});               
     }         
     public Atributo<String> cte() {return cte;}
     public Atributo<String> val() {return val;}
     public Atributo<Integer> fila() {return fila;}
     public Atributo<Integer> col() {return col;}
     protected abstract String cte_exp();
     protected abstract String val_exp();
     protected abstract Integer fila_exp();
     protected abstract Integer col_exp();
     private Atributo<String> cte;
     private Atributo<String> val;
     private Atributo<Integer> fila;
     private Atributo<Integer> col;
   }      

   abstract public class Exp  {
      protected Exp() {
        tsh = new Atributo<TS>();
        tsh.fijaExpresion(new ExpSem<TS>() { public TS val() {
            return ctx.tsh_exp();
        }}); 
       err = new Atributo<Error>();
       err.fijaExpresion(new ExpSem<Error>() {public Error val() {
           return err_exp();
       }});    
       cod = new Atributo<List<Instruccion>>();
       cod.fijaExpresion(new ExpSem<List<Instruccion>>() {public List<Instruccion> val() {
           return cod_exp();
       }});               
      }
      public Atributo<TS> tsh() {return tsh;} 
      public Atributo<Error> err() {return err;} 
      public Atributo<List<Instruccion>> cod() {return cod;} 
      protected abstract Error err_exp();
      protected abstract List<Instruccion> cod_exp();
      public void registraCtx(ExpCtx ctx) {this.ctx = ctx;}
      private ExpCtx ctx;
      private Atributo<TS> tsh;
      private Atributo<Error> err;
      private Atributo<List<Instruccion>> cod;
   }
   public interface ExpCtx {
     public TS tsh_exp();
   }

  abstract public class Exp0 extends Exp {}
  abstract public class Exp1 extends Exp {}
   
      
     /*
       Sent ::= Decs & Exp0
           Exp0.tsh = Decs.ts
           Sent.err = Decs.err ++ Exp0.err
           Sent.cod = Exp0.cod
     */
   public class SentR1 extends Sent {
      public SentR1(Decs decs, Exp0 exp0) {
         super(); 
         this.decs = decs;
         this.exp0 = exp0;
         
         exp0.registraCtx(new ExpCtx() {public TS tsh_exp() {
                 return SentR1.this.decs.ts().val();
         }});
   
         err().ponDependencias(decs.err(),exp0.err());
         cod().ponDependencias(exp0.cod());
         exp0.tsh().ponDependencias(decs.ts());
    
      }
      protected final Error err_exp() {
         return joinErrors(decs.err().val(),exp0.err().val());
      }
      protected final List<Instruccion> cod_exp() {
         return exp0.cod().val();
      }
      private Decs decs;
      private Exp0 exp0;
   }
   
   public class SentR1Debug extends SentR1 {
      private final static String REGLA="Sent ::= Decs & Exp0"; 
      public SentR1Debug(Decs decs, Exp0 exp0) {
        super(decs,exp0);  
        err().fijaDescripcion(REGLA+"|Sent.err");
        cod().fijaDescripcion(REGLA+"|Sent.cod");
        exp0.tsh().fijaDescripcion(REGLA+"|Exp0.tsh");
      }
   }
  
     /*
       Decs ::= Decs ; Dec
           Decs(0).ts = aniadeCte(Decs(1).ts,Dec.cte.lex,Dec.val)
           Decs(0).err = Decs(1).err ++ 
		                 errorSi(estaEn(Dec.cte,Decs(1).ts),Dec.fila,Dec.col,"Constante duplicada: "+Dec.cte)
       */
   public class DecsR1 extends Decs {
      public DecsR1(Decs decs_1, Dec dec) {
         super(); 
         this.decs_1 = decs_1;
         this.dec = dec;
         ts().ponDependencias(decs_1.ts(),dec.cte(),dec.val());
         err().ponDependencias(decs_1.ts(),decs_1.err(),dec.cte(),dec.fila(),dec.col());   

       }
      protected final TS ts_exp() {
         return aniadeCte(decs_1.ts().val(),dec.cte().val(),dec.val().val());
      }
      protected final Error err_exp() {
         return joinErrors(decs_1.err().val(), errorSi(estaEn(dec.cte().val(),decs_1.ts().val()),
			                dec.fila().val(),dec.col().val(),"Constante duplicada:"+dec.cte().val()));
      }
      private Decs decs_1;
      private Dec dec;  
   }
   
   public class DecsR1Debug extends DecsR1 {
      private final static String REGLA="Decs ::= Decs ; Dec"; 
      public DecsR1Debug(Decs decs_1, Dec dec) {
        super(decs_1,dec);  
        ts().fijaDescripcion(REGLA+"|Decs(0).ts");
        err().fijaDescripcion(REGLA+"|Decs(0).err");
      }  
   }

     /*
       Decs ::= Dec
           Decs.ts = aniadeCte(creaTS(),Dec.cte,Dec.val)
           Decs.err = noError()
       */
   public class DecsR2 extends Decs {  
      public DecsR2(Dec dec) {
         super(); 
         this.dec = dec;
         ts().ponDependencias(dec.cte(),dec.val());
      }
      protected final TS ts_exp() {
         return aniadeCte(creaTS(),dec.cte().val(),dec.val().val());
      }
      protected final Error err_exp() {
         return noError(); 
      }
      private Dec dec;  
   }
   
   public class DecsR2Debug extends DecsR2 {
     private final static String REGLA="Decs::=Dec";  
     public DecsR2Debug(Dec dec) {
        super(dec);
        ts().fijaDescripcion(REGLA+"|Decs.ts");
        err().fijaDescripcion(REGLA+"|Decs.err");
     }  
   }
   

     /*
       Dec ::= id = num
           Dec.cte = id.lex
           Dec.val = num.lex
		   Dec.fila = id.fila
		   Dec.col = id.col
		   
       */
   public class DecR1 extends Dec {
      public DecR1(Token id, Token num) {
         super(); 
         this.id = id;
         this.num = num;
      }
      protected final String cte_exp() {
         return id.lex();
      }
      protected final String val_exp() {
         return num.lex();
      }
      protected final Integer fila_exp() {
        return new Integer(id.fila());
      }
      protected final Integer col_exp() {
        return new Integer(id.col());
      }
      private Token id;  
      private Token num;  
   }
   public class DecR1Debug extends DecR1 {
      private final static String REGLA="Dec ::= id = num"; 
      public DecR1Debug(Token id, Token num) {
        super(id,num);
        cte().fijaDescripcion(REGLA+"|Dec.cte");
        val().fijaDescripcion(REGLA+"|Dec.val");
        fila().fijaDescripcion(REGLA+"|Dec.fila");
        col().fijaDescripcion(REGLA+"|Dec.col");
      } 
   }   


     /*
       Exp0 ::= Exp0 + Exp1
           Exp0(1).tsh = Exp0(0).tsh
           Exp1.tsh = Exp0(0).tsh
           Exp0(0).err = Exp0(1).err ++ Exp1.err
           Exp0(0).cod = Exp0(0).codd || Exp1.cod || suma
       */
   public class Exp0R1 extends Exp0 {
      public Exp0R1(Exp0 exp0_1, Exp1 exp1) {
         this.exp0_1= exp0_1;
         this.exp1 = exp1;
         exp0_1.registraCtx(new ExpCtx() { public TS tsh_exp() {
           return Exp0R1.this.tsh().val();
         }});
         exp1.registraCtx(new ExpCtx() { public TS tsh_exp() {
            return Exp0R1.this.tsh().val();
         }});
         err().ponDependencias(exp0_1.err(),exp1.err());
         cod().ponDependencias(exp0_1.cod(),exp1.cod());
         exp0_1.tsh().ponDependencias(tsh());
         exp1.tsh().ponDependencias(tsh());
      }
      protected final Error err_exp() {
        return joinErrors(exp0_1.err().val(),exp1.err().val());
      }
      protected final List<Instruccion> cod_exp() {
         return concat(exp0_1.cod().val(),concat(exp1.cod().val(),suma()));
      }
      private Exp0 exp0_1;
      private Exp1 exp1;
   } 
   public class Exp0R1Debug extends Exp0R1 {
       private final static String REGLA="Exp0 ::= Exp0 + Exp1";
       public Exp0R1Debug(Exp0 exp0_1, Exp1 exp1) {
          super(exp0_1,exp1);
          err().fijaDescripcion(REGLA+"|Exp0(0).err");
          cod().fijaDescripcion(REGLA+"|Exp0(0).cod");
          exp0_1.tsh().fijaDescripcion(REGLA+"|Exp0(1).tsh");
          exp1.tsh().fijaDescripcion(REGLA+"|Exp1.tsh");
      }
   }   


     /*
       Exp0 ::= Exp1 * Exp1
           Exp1(0).tsh = Exp0.tsh
           Exp1(1).tsh = Exp0.tsh           
           Exp0.err = Exp1(0).err ++ Exp1(1).err 
           Exp0.cod = Exp1(0).cod || Exp1(1).cod || mul
       */
   public class Exp0R2 extends Exp0 {
      public Exp0R2(Exp1 exp1_0, Exp1 exp1_1) {
         this.exp1_0= exp1_0;
         this.exp1_1 = exp1_1;
         exp1_0.registraCtx(new ExpCtx() { public TS tsh_exp() {
           return Exp0R2.this.tsh().val();
         }});
         exp1_1.registraCtx(new ExpCtx() { public TS tsh_exp() {
            return Exp0R2.this.tsh().val();
         }});
         err().ponDependencias(exp1_0.err(),exp1_1.err());
         cod().ponDependencias(exp1_0.cod(),exp1_1.cod());
         exp1_0.tsh().ponDependencias(tsh());
         exp1_1.tsh().ponDependencias(tsh());
      }
      protected final Error err_exp() {
        return joinErrors(exp1_0.err().val(),exp1_1.err().val());
      }
      protected final List<Instruccion> cod_exp() {
         return concat(exp1_0.cod().val(),concat(exp1_1.cod().val(),mul()));
      }
      private Exp1 exp1_0;
      private Exp1 exp1_1;
   } 
   public class Exp0R2Debug extends Exp0R2 {
       private final static String REGLA="Exp0 ::= Exp1 * Exp1";
       public Exp0R2Debug(Exp1 exp1_0, Exp1 exp1_1) {
          super(exp1_0,exp1_1);
          err().fijaDescripcion(REGLA+"|Exp0.err");
          cod().fijaDescripcion(REGLA+"|Exp0.cod");
          exp1_0.tsh().fijaDescripcion(REGLA+"|Exp1(0).tsh");
          exp1_1.tsh().fijaDescripcion(REGLA+"|Exp1(1).tsh");
      }
   }   

     /*
       Exp0 ::= Exp1
           Exp1.tsh = Exp0.tsh
           Exp0.err = Exp1.err 
           Exp0.cod = Exp1.cod
       */
   public class Exp0R3 extends Exp0 {
      public Exp0R3(Exp1 exp1) {
         this.exp1 = exp1;
         exp1.registraCtx(new ExpCtx() {public TS tsh_exp() {
           return Exp0R3.this.tsh().val();
         }});
         err().ponDependencias(exp1.err());
         cod().ponDependencias(exp1.cod());
         exp1.tsh().ponDependencias(tsh());
      }
      protected final Error err_exp() {
        return exp1.err().val();
      }
      public List<Instruccion> cod_exp() {
        return exp1.cod().val();
      }
      private Exp1 exp1;
   }
   public class Exp0R3Debug extends Exp0R3 {
     private final static String REGLA="Exp0 ::= Exp1";
     public Exp0R3Debug(Exp1 exp1) {
         super(exp1);
         err().fijaDescripcion(REGLA+"|Exp0.err");
         cod().fijaDescripcion(REGLA+"|Exp0.cod");
         exp1.tsh().fijaDescripcion(REGLA+"|Exp1.tsh");
     }
   }   
     /*
       Exp1 ::= num
           Exp1.err = noError() 
           Exp1.cod = apila(num.lex)
       */
   public class Exp1R1 extends Exp1 {
      public Exp1R1(Token num) {
         this.num = num;
      }
      protected final Error err_exp() {
         return noError();
      }
      protected final List<Instruccion> cod_exp() {
        return single(apila(num.lex()));
      }
      private Token num;
   }
   public class Exp1R1Debug extends Exp1R1 {
     private final static String REGLA="Exp1 ::= num";  
     public Exp1R1Debug(Token num) {
         super(num);
         err().fijaDescripcion(REGLA+"|Exp1.err");
         cod().fijaDescripcion(REGLA+"|Exp1.cod");
     }
   }   

     /*
       Exp1 ::= id
           Exp1.err = errorSi(not estaEn(id.lex,Exp1.tsh),id.fila,id.col,"Identificador no declarado") 
           Exp1.cod = apila(valDe(id.lex,Exp1.tsh))
       */
   
   public class Exp1R2 extends Exp1 {
      public Exp1R2(Token id) {
         this.id = id;
         cod().ponDependencias(tsh()); 
         err().ponDependencias(tsh());
      }
      protected final Error err_exp() {
         return errorSi(! estaEn(id.lex(),tsh().val()),id.fila(),id.col(),"Identificador no declarado:"+id.lex());
      }
      protected final List<Instruccion> cod_exp() {
        return single(apila(valDe(id.lex(),tsh().val())));
      }
      private Token id;
   }
   public class Exp1R2Debug extends Exp1R2 {
     private final static String REGLA="Exp1 ::= id";  
     public Exp1R2Debug(Token num) {
         super(num);
         err().fijaDescripcion(REGLA+"|Exp1.err");
         cod().fijaDescripcion(REGLA+"|Exp1.cod");
     }
   }   

  
     /*
       Exp1 ::= (Exp0)
           Exp0.tsh = Exp1.tsh
           Exp1.err = Exp0.err 
           Exp1.cod = Exp0.cod
       */
 public class Exp1R3 extends Exp1 {
      public Exp1R3(Exp0 exp0) {
         this.exp0 = exp0;
         exp0.registraCtx(new ExpCtx() {public TS tsh_exp() {
           return Exp1R3.this.tsh().val();
         }});
         err().ponDependencias(exp0.err());
         cod().ponDependencias(exp0.cod());
         exp0.tsh().ponDependencias(tsh());
      }
      protected final Error err_exp() {
        return exp0.err().val();
      }
      public List<Instruccion> cod_exp() {
        return exp0.cod().val();
      }
      private Exp0 exp0;
   }
   public class Exp1R3Debug extends Exp1R3 {
     private final static String REGLA="Exp1 ::= (Exp0)";
     public Exp1R3Debug(Exp0 exp0) {
         super(exp0);
         err().fijaDescripcion(REGLA+"|Exp1.err");
         cod().fijaDescripcion(REGLA+"|Exp1.cod");
         exp0.tsh().fijaDescripcion(REGLA+"|Exp0.tsh");
     }
   }   
 
   public TS aniadeCte(TS ts, String cte, String val){
        return ts.aniade(cte,val);         
   } 
   public boolean estaEn(String cte, TS ts){
        return ts.estaEn(cte);         
   } 
   public String valDe(String cte, TS ts){
        return ts.valDe(cte);         
   } 
   public TS creaTS() {
        return new TS();
   }
   public List<Instruccion> concat(List<Instruccion> c1, List<Instruccion> c2) {
       c1.addAll(c2);
       return c1;
   }
   public List<Instruccion> concat(List<Instruccion> c1, Instruccion i) {
      c1.add(i);
      return c1;
   }
   public Instruccion suma() {
       return Instruccion.nuevaISuma();  
   }
   public Instruccion mul() {
       return Instruccion.nuevaIMul();  
   }
   public Instruccion apila(String arg) {
       return Instruccion.nuevaIApila(arg);  
   }
   public List<Instruccion> single(Instruccion i) {
      List<Instruccion> is = new LinkedList<Instruccion>();
      is.add(i);
      return is;
   }
   public Error noError() {
     return new Error();
   }
   public Error joinErrors(Error e1,Error e2) {
     return new Error(e1,e2);
   }
   public Error errorSi(boolean condicion, int fila, int col, String msg) {
     if (condicion) {
	    return new Error("("+fila+","+col+") ERROR CONTEXTUAL: "+msg);
	 }
     else 
	    return new Error();
   }
   
   private void requerido(String ctx) {identado++; identa(); System.out.println("REQUERIDO "+ctx);}
   private <O> O valor(String ctx, O val) {identa(); System.out.println("VALOR DE "+ctx+":"+val); identado--; return val;}
   private void identa() {
     for (int i=0; i < identado; i++) 
         System.out.print(".");   
   }
   
   private int identado;
      
   public static void main(String[] args) {
      TinyGA ga = new TinyGA();
           
      
      TinyGA.Sent sent = ga.new SentR1(
                           ga.new DecsR1(
                             ga.new DecsR2(                                                                      
                               ga.new DecR1(
                                  new Token(CatLexica.ID,"x"),
                                  new Token(CatLexica.NUM,"5"))),
                             ga.new DecR1(
                                new Token(CatLexica.ID,"y"),
                                new Token(CatLexica.NUM,"6"))),
                           ga.new Exp0R1( 
                             ga.new Exp0R3(
                                  ga.new Exp1R2(
                                       new Token(CatLexica.ID,"y"))),
                                  ga.new Exp1R2(
                                       new Token(CatLexica.ID,"x"))));

      
      System.out.println(sent.err());                                           
      System.out.println(sent.cod());                                           
   }   
}
